// Image
// (c) jimon game studio

#include "CImage.h"
#include "ILogManager.h"
#include "jeThreads.h"
#include <memory.h>

namespace je
{
	namespace video
	{
		//! Constructor
		CImage::CImage(const void * Data,const jeVector2di & ImgSize,ImageColorFormat Format)
			:ColorFormat(Format),Size(ImgSize),Locked(false)
		{
			JENEW_A(Image,u8,Size.X * Size.Y * GetBytesPerPixel())

			if(Data)
				#ifdef JE_MSVC
				memcpy_s(Image,Size.X * Size.Y * GetBytesPerPixel(),Data,Size.X * Size.Y * GetBytesPerPixel());
				#else
				memcpy(Image,Data,Size.X * Size.Y * GetBytesPerPixel());
				#endif
			else
				memset(Image,0,Size.X * Size.Y * GetBytesPerPixel());
		}

		//! Destructor
		CImage::~CImage()
		{
			JEDELETE_A((u8*)Image);
			Image = NULL;
		}

		//! Lock Image
		void CImage::Lock()
		{
			JEASSERT(!Locked)
			Locked = true;
		}

		//! UnLock Image
		void CImage::UnLock()
		{
			JEASSERT(Locked)
			Locked = false;
		}

		//! Return true if locked
		u1 CImage::IsLocked()
		{
			return Locked;
		}

		//! Get Pointer to data
		void * CImage::GetPtr()
		{
			JEASSERT(Locked)
			return Image;
		}

		//! Draw Bitmap on image
		void CImage::DrawBitmapOnImage(void * Bitmap,const jeVector2di & DrawPosition,const jeVector2di & BitmapOffset,const jeVector2di & BitmapCopySize,const jeVector2di & BitmapSize,ImageColorFormat BitmapFormat,u1 WithBlending)
		{
			if((DrawPosition.X == 0) && (DrawPosition.Y == 0) && (BitmapOffset.X == 0) && (BitmapOffset.Y == 0) && (BitmapSize == Size) && (BitmapCopySize == Size) && (ColorFormat == BitmapFormat))
			{
				#ifdef JE_MSVC
				memcpy_s(Image,Size.X * Size.Y * GetBytesPerPixel(),Bitmap,Size.X * Size.Y * GetBytesPerPixel());
				#else
				memcpy(Image,Bitmap,Size.X * Size.Y * GetBytesPerPixel());
				#endif
			}
			else
			{
				if(DrawPosition.X >= Size.X)
					return;

				if(DrawPosition.Y >= Size.Y)
					return;

				if(BitmapCopySize.X < 0)
					return;

				if(BitmapCopySize.Y < 0)
					return;

				if(WithBlending)
				{
					for(s32 Y=DrawPosition.Y;(Y<(BitmapCopySize.Y+DrawPosition.Y))&&(Y<Size.Y);Y++)
					{
						for(s32 X=DrawPosition.X;(X<(BitmapCopySize.X+DrawPosition.X))&&(X<Size.X);X++)
						{
							u8 * DestMem = (u8*)Image+(Y*Size.X+X)*GetBytesPerPixel();
							u8 * SrcMem = (u8*)Bitmap+((Y-DrawPosition.Y+BitmapOffset.Y)*BitmapSize.X+BitmapOffset.X+X-DrawPosition.X)*GetBytesPerPixel();

							f32 Alpha = ((f32)SrcMem[0]) / 255.0f;
							f32 InvAlpha = 1.0f - Alpha;

							for(u8 i=0;i<4;i++)
								DestMem[i] = (u8)jeMin(InvAlpha * (f32)DestMem[i] + Alpha * (f32)SrcMem[i],255.0f);
						}
					}
				}
				else
				{
					for(s32 Y=DrawPosition.Y;(Y<(BitmapCopySize.Y+DrawPosition.Y))&&(Y<Size.Y);Y++)
					{
						c8 * DestMem = (c8*)Image+(Y*Size.X+DrawPosition.X)*GetBytesPerPixel();
						c8 * SrcMem = (c8*)Bitmap+((Y-DrawPosition.Y+BitmapOffset.Y)*BitmapSize.X+BitmapOffset.X)*GetBytesPerPixel();

						u32 CopySize = 0;
						if((BitmapCopySize.X+DrawPosition.X)<=Size.X)
							CopySize = BitmapCopySize.X;
						else
							CopySize = Size.X-DrawPosition.X;

						CopySize*=GetBytesPerPixel();

						memcpy(DestMem,SrcMem,CopySize);
					}
				}
			}
		}

		//! Draw Image on image
		void CImage::DrawImageOnImage(IImage * CopyImage,const jeVector2di & DrawPosition,const jeVector2di & ImageOffset,const jeVector2di & DrawSize,u1 WithBlending)
		{
			u1 ImgLockFlag = CopyImage->IsLocked();
			if(!ImgLockFlag)
				CopyImage->Lock();
			if((DrawSize.X == 0) && (DrawSize.Y == 0))
				DrawBitmapOnImage(CopyImage->GetPtr(),DrawPosition,ImageOffset,CopyImage->GetSize(),CopyImage->GetSize(),CopyImage->GetFormat(),WithBlending);
			else
				DrawBitmapOnImage(CopyImage->GetPtr(),DrawPosition,ImageOffset,DrawSize,CopyImage->GetSize(),CopyImage->GetFormat(),WithBlending);
			if(!ImgLockFlag)
				CopyImage->UnLock();
		}

		//! Return Image Format
		IImage::ImageColorFormat CImage::GetFormat()
		{
			return ColorFormat;
		}

		//! Return size of image
		const jeVector2di & CImage::GetSize()
		{
			return Size;
		}

		//! Return Bytes per pixel
		u32 CImage::GetBytesPerPixel()
		{
			switch(ColorFormat)
			{
			case ICF_R8G8B8A8:
				return 4;
			default:
				return 0;
			}
		}

		//! Flip Image Vertical, lock and unlock if image unlocked
		void CImage::FlipVertical()
		{
			u1 NeedUnlock = !IsLocked();

			if(NeedUnlock)
				Lock();

			for(s32 x = 0; x<Size.X/2 ;x++)
				for(s32 y = 0; y<Size.Y ;y++)
				{
					u32 Bytes = GetBytesPerPixel();

					switch(Bytes)
					{
					case 4:
						{
							u32 * p1 = &((u32*)Image)[x+y*Size.X];
							u32 * p2 = &((u32*)Image)[Size.X-1-x+y*Size.X];
							u32 p3 = *p1;
							*p1 = *p2;
							*p2 = p3;
							break;
						}

					}
				}

			if(NeedUnlock)
				UnLock();
		}

		//! Flip Image Horizontal, lock and unlock if image unlocked
		void CImage::FlipHorizontal()
		{
			u1 NeedUnlock = !IsLocked();

			if(NeedUnlock)
				Lock();

			for(s32 y = 0; y<Size.Y/2 ;y++)
				for(s32 x = 0; x<Size.X ;x++)
				{
					u32 Bytes = GetBytesPerPixel();

					switch(Bytes)
					{
					case 4:
						{
							u32 * p1 = &((u32*)Image)[x+y*Size.X];
							u32 * p2 = &((u32*)Image)[x+(Size.Y-1-y)*Size.X];
							u32 p3 = *p1;
							*p1 = *p2;
							*p2 = p3;
							break;
						}

					}
				}

			if(NeedUnlock)
				UnLock();
		}

		//! Resize
		IImage * CImage::Copy(const jeVector2di & CopyPosition,const jeVector2di & CopySize)
		{
			jeVector2di TempPosition = CopyPosition;
			jeVector2di TempSize = CopySize;

			u8 * Temp = NULL;
			u32 Bytes = GetBytesPerPixel();
			JENEW_A(Temp,u8,TempSize.X*TempSize.Y*Bytes)
			memset(Temp,0,TempSize.X*TempSize.Y*Bytes);

			for(s32 y=0;y<TempSize.Y;y++)
				#ifdef JE_MSVC
				memcpy_s(Temp+y*TempSize.X*Bytes,TempSize.X*Bytes,(u8*)Image+(CopyPosition.X+(y+CopyPosition.Y)*Size.X)*Bytes,TempSize.X*Bytes);
				#else
				memcpy(Temp+y*TempSize.X*Bytes,(u8*)Image+(CopyPosition.X+(y+CopyPosition.Y)*Size.X)*Bytes,TempSize.X*Bytes);
				#endif

			CImage * Img = NULL;
			JENEW(Img,CImage(Temp,TempSize,ColorFormat))

			JEDELETE_A(Temp)

			return Img;
		}

		//! Create Image
		IImage * CreateImage(const jeVector2di & Size,IImage::ImageColorFormat Format,const void * PixelData)
		{
			IImage * Image = NULL;
			JENEW(Image,CImage(PixelData,Size,Format))
			return Image;
		}


	}
}
